#include "Lua.h"
#include <stdarg.h>
#include "Log.h"
#include <string.h>


int LuaAtpanic(lua_State* L)
{
	const char* pStr = luaL_checkstring(L,-1);
	if ( pStr ){
		return 1;
	}
	return 0;
}

bool Lua::Init()
{
	m_pL = luaL_newstate();
	if ( !m_pL )
		return false;
	luaL_openlibs(m_pL);
	lua_CFunction lcf = lua_atpanic(m_pL, LuaAtpanic);
	m_nIndex = lua_gettop(m_pL);
	return true;
}

bool Lua::DoLuaFile( char* name )
{
	if ( luaL_dofile(m_pL, name) )
		return false;
	return true;
}

bool Lua::CallLuaFunc( char* name, char* fmt, ... )
{
	lua_getglobal(m_pL, name);
	int narg = 0;
	int nres = 0; 
	va_list vl;
	va_start(vl, fmt);
	while (*fmt) {    /* push arguments */
		bool bError = false;
		bool bEndArg= false;
		switch (*fmt++) {
		case 'b':  /* bool argument */
			lua_pushboolean(m_pL, va_arg(vl, bool));
			break;
		case 'd':  /* double argument */
			lua_pushnumber(m_pL, va_arg(vl, double));
			break;
		case 'i':  /* int argument */
			lua_pushnumber(m_pL, va_arg(vl, int));
			break;
		case 's':  /* string argument */
			lua_pushstring(m_pL, va_arg(vl, char *));
			break;
		case 'p':  /* userdata argument */
			lua_pushlightuserdata(m_pL, va_arg(vl, void *));
			break;
		case '>':
			bEndArg = true;
			break;
		default:
			bError  = true;
		}
		if ( bEndArg )
			break;
		if ( bError || !lua_checkstack(m_pL, 1) )
			return false;
		narg++;
	}

	nres = strlen(fmt);
	if (lua_pcall(m_pL, narg, nres, 0) != 0)
		return false;

	nres = -nres;     /* stack index of first result */
	while (*fmt) {    /* get results */
		switch (*fmt++) {
		case 'b':  /* bool result */
			if (!lua_isboolean(m_pL, nres))
				return false;
			*va_arg(vl, bool *) = lua_toboolean(m_pL, nres)? true: false;
			break;
		case 'd':  /* double result */
			if (!lua_isnumber(m_pL, nres))
				return false;
			*va_arg(vl, double *) = lua_tonumber(m_pL, nres);
			break;
		case 'i':  /* int result */
			if (!lua_isnumber(m_pL, nres))
				return false;
			*va_arg(vl, int *) = (int)lua_tonumber(m_pL, nres);
			break;
		case 's':  /* string result */
			if (!lua_isstring(m_pL, nres))
				return false;
			*va_arg(vl, char **) = (char*)lua_tostring(m_pL, nres);
			break;
		case 'p':  /* userdata result */
			if (!lua_islightuserdata(m_pL, nres))
				return false;
			*va_arg(vl, void **) = (void*)lua_touserdata(m_pL, nres);
			break;
		default:
			return false;
		}
		nres++;
	}
	va_end(vl);
	return true;
}

void Lua::RegsterCFunc(const char* name, lua_CFunction cFunc )
{
	lua_pushcfunction(m_pL, cFunc);
	lua_setglobal(m_pL, name);
}

void Lua::RegsterCFunc( luaL_Reg* cFuncList, int size )
{
	for ( int i = 0; i < size; i++ )
		RegsterCFunc(cFuncList[i].name, cFuncList[i].func);
}

void Lua::SetLuaVarBool( char* name, bool value )
{
	lua_pushboolean(m_pL, value);
	lua_setglobal(m_pL, name);
}

void Lua::SetLuaVarInt( char* name, int value )
{
	lua_pushinteger(m_pL, value);
	lua_setglobal(m_pL, name);
}

void Lua::SetLuaVarDouble( char* name, double value )
{
	lua_pushnumber(m_pL, value);
	lua_setglobal(m_pL, name);
}

void Lua::SetLuaVarString( char* name, char* value, uint len)
{
	lua_pushlstring(m_pL, value, len);
	lua_setglobal(m_pL, name);
}

void Lua::SetLuaVarUserdata( char* name, void* value )
{
	lua_pushlightuserdata(m_pL, value);
	lua_setglobal(m_pL, name);
}

bool Lua::GetLuaVarBool( char* name, bool& value )
{
	bool ret = false;
	int index = lua_gettop (m_pL);
	lua_getglobal(m_pL, name);
	if ( lua_isboolean(m_pL, -1) ){
		value = lua_toboolean(m_pL, -1)? true: false;
		ret = true;
	}
	lua_settop(m_pL, index);
	return ret;
}

bool Lua::GetLuaVarInt( char* name, int& value )
{
	bool ret = false;
	int index = lua_gettop (m_pL);
	lua_getglobal(m_pL, name);
	if ( lua_isnumber(m_pL, -1) ){
		value = lua_tointeger(m_pL, -1);
		ret = true;
	}
	lua_settop(m_pL, index);
	return ret;
}

bool Lua::GetLuaVarDouble( char* name, double& value )
{
	bool ret = false;
	int index = lua_gettop (m_pL);
	lua_getglobal(m_pL, name);
	if ( lua_isnumber(m_pL, -1) ){
		value = lua_tonumber(m_pL, -1);
		ret = true;
	}
	lua_settop(m_pL, index);
	return ret;
}

bool Lua::GetLuaVarString( char* name, char* value, uint len )
{
	bool ret = false;
	int index = lua_gettop (m_pL);
	lua_getglobal(m_pL, name);
	if ( lua_isstring(m_pL, -1) ){
		size_t luaStrLen = 0;
		const char* pBuff = lua_tolstring(m_pL, -1, &luaStrLen);
		len = len>luaStrLen? luaStrLen: len;
		strncpy(value, pBuff, len);
		ret = true;
	}
	lua_settop(m_pL, index);
	return ret;
}

bool Lua::GetLuaVarUserdata( char* name, void** value )
{
	bool ret = false;
	int index = lua_gettop (m_pL);
	lua_getglobal(m_pL, name);
	if ( lua_islightuserdata(m_pL, -1) ){
		*value = lua_touserdata(m_pL, -1);
		ret = true;
	}
	lua_settop(m_pL, index);
	return ret;
}

int Lua::GetTopIndex()
{
	return lua_gettop(m_pL);
}

void Lua::SetTopIndex( int index )
{
	lua_settop(m_pL, index);
}

char* Lua::GetLuaError()
{
	if ( !lua_isstring(m_pL, -1) )
		return NULL;
	return (char*)lua_tostring(m_pL, -1);
}

void Lua::ResetIndex()
{
	lua_settop(m_pL, m_nIndex);
}

bool Lua::SetLuaVarTable( char* name, char* fmt, ... )
{
	size_t tLen = strlen(fmt);
	if ( tLen <= 0 || name == NULL )
		return false;
	int index = lua_gettop(m_pL);
	lua_getglobal(m_pL, name);
	if ( lua_istable(m_pL, -1) == 0 ){
		lua_pop(m_pL, 1);
		lua_createtable(m_pL, 10, 10);
	}
	va_list vl;
	va_start(vl, fmt);
	for ( size_t i = 1; i <= tLen; i++ )
	{
		switch (*fmt++) {
		case 'b':  /* bool argument */
			lua_pushboolean(m_pL, va_arg(vl, bool));
			lua_rawseti(m_pL, -2, i);
			break;
		case 'd':  /* double argument */
			lua_pushnumber(m_pL, va_arg(vl, double));
			lua_rawseti(m_pL, -2, i);
			break;
		case 'i':  /* int argument */
			lua_pushnumber(m_pL, va_arg(vl, int));
			lua_rawseti(m_pL, -2, i);
			break;
		case 's':  /* string argument */
			lua_pushstring(m_pL, va_arg(vl, char *));
			lua_rawseti(m_pL, -2, i);
			break;
		case 'p':  /* userdata argument */
			lua_pushlightuserdata(m_pL, va_arg(vl, void *));
			lua_rawseti(m_pL, -2, i);
			break;
		default:
			lua_settop(m_pL, index);
			return false;
		}
		if ( !lua_checkstack(m_pL, 1) ){
			lua_settop(m_pL, index);
			return false;
		}
	}
	lua_setglobal(m_pL, name);
	return true;
}

bool Lua::GetLuaVarTable( char* name, char* fmt, ... )
{
	size_t tLen = strlen(fmt);
	if ( tLen <= 0 || name == NULL )
		return false;
	lua_getglobal(m_pL, name);
	if ( lua_istable(m_pL, -1) == 0 )
		return false;

	va_list vl;
	va_start(vl, fmt);
	for ( size_t i = 1; i <= tLen; i++ )
	{
		switch (*fmt++) {
		case 'b':  /* bool argument */
			lua_rawgeti(m_pL, -1, i);
			if ( !lua_isboolean(m_pL, -1) )
				return false;
			*va_arg(vl, bool*) = lua_toboolean(m_pL, -1)? true: false;
			lua_pop(m_pL, 1);
			break;
		case 'd':  /* double argument */
			lua_rawgeti(m_pL, -1, i);
			if ( !lua_isnumber(m_pL, -1) )
				return false;
			*va_arg(vl, double*) = (double)lua_tonumber(m_pL, -1);
			lua_pop(m_pL, 1);
			break;
		case 'i':  /* int argument */
			lua_rawgeti(m_pL, -1, i);
			if ( !lua_isnumber(m_pL, -1) )
				return false;
			*va_arg(vl, int*) = (int)lua_tonumber(m_pL, -1);
			lua_pop(m_pL, 1);
			break;
		case 's':  /* string argument */
			lua_rawgeti(m_pL, -1, i);
			if ( !lua_isstring(m_pL, -1) )
				return false;
			*va_arg(vl, char**) = (char*)lua_tostring(m_pL, -1);
			lua_pop(m_pL, 1);
			break;
		case 'p':  /* userdata argument */
			lua_rawgeti(m_pL, -1, i);
			if ( !lua_islightuserdata(m_pL, -1) )
				return false;
			*va_arg(vl, void**) = (void*)lua_touserdata(m_pL, -1);
			lua_pop(m_pL, 1);
			break;
		default:
			return false;
		}
		if ( !lua_checkstack(m_pL, 1) )
			return false;
	}
	return true;
}
